// +FHDR------------------------------------------------------------
//                 Copyright (c) 2022 .
//                       ALL RIGHTS RESERVED
// -----------------------------------------------------------------
// Filename      : xxx_driver.sv
// Author        : 
// Created On    : 2022-08-25 15:07
// Last Modified : 2023-12-01 11:09 by ICer
// -----------------------------------------------------------------
// Description:
//
//
// -FHDR------------------------------------------------------------

`ifndef __XXX_DRIVER_SV__
`define __XXX_DRIVER_SV__

class xxx_driver extends uvm_driver #(xxx_transaction);
  bit active_send_en = 1;
	virtual xxx_interface vif;
  `ifdef UTILS_TEST
  uvm_analysis_port #(xxx_transaction) ap;
  `endif
	`uvm_component_utils(xxx_driver)

	extern function new(string name = "xxx_driver", uvm_component parent=null);
	extern virtual function void build_phase(uvm_phase phase);
	extern virtual task reset_phase(uvm_phase phase);
	extern virtual task main_phase(uvm_phase phase);
	extern virtual task drive_pkt(xxx_transaction pkt);
	extern virtual task drive_idle();

endclass: xxx_driver

function xxx_driver::new(string name = "xxx_driver", uvm_component parent=null);
	super.new(name, parent);
endfunction: new

function void xxx_driver::build_phase(uvm_phase phase);
	super.build_phase(phase);
	`uvm_info("xxx_driver", "build_phase is called", UVM_HIGH);
	if(!uvm_config_db #(virtual xxx_interface)::get(this, "", "vif", vif))begin
		`uvm_fatal("xxx_driver", "virtual interface get fatal");
	end
  `ifdef UTILS_TEST
  ap   = new("ap", this);
  `endif
endfunction: build_phase

task xxx_driver::reset_phase(uvm_phase phase);
  phase.raise_objection(this);
  drive_idle();
  phase.drop_objection(this);
endtask: reset_phase


task xxx_driver::main_phase(uvm_phase phase);
	if(active_send_en == 1) phase.raise_objection(this);
	`uvm_info("xxx_driver", "main_phase is called", UVM_HIGH);
    
  this.drive_idle();
  while(!this.vif.rst_n) @this.vif.drv;

  @this.vif.drv;
	while(1)begin
    xxx_transaction req;
		seq_item_port.try_next_item(req);
		if(req == null) begin
      if (active_send_en == 1) break;
      else @this.vif.drv;
    end
		else begin
	    `uvm_info("xxx_driver", "drive a pkt start", UVM_HIGH);
      `ifdef UTILS_TEST
      ap.write(req.clone());
      `endif
			this.drive_pkt(req);
			seq_item_port.item_done();
		end
	end
  this.drive_idle();

  #1000;
	if(active_send_en == 1) phase.drop_objection(this);
endtask: main_phase

task xxx_driver::drive_pkt(xxx_transaction pkt);
	pkt.pack();
	
    while(1) begin
        this.vif.drv.valid <= 'd1;
        this.vif.drv.data <= pkt.data;
        this.vif.drv.crc <= pkt.crc;
        this.vif.drv.last <= pkt.last;
        @this.vif.drv;

        if(this.vif.drv.ready === 1'd1) begin
            this.vif.drv.valid <= 'd0;
            this.vif.drv.data <= 'dx;
            this.vif.drv.crc <= 'dx;
            this.vif.drv.last <= 'dx;
            repeat(pkt.cycle_post)begin
                this.drive_idle();
            end
            break;
        end
	end
endtask: drive_pkt

task xxx_driver::drive_idle();
  @this.vif.drv;
	this.vif.drv.valid <= 'd0;
    this.vif.drv.data <= 'dx;
    this.vif.drv.crc <= 'dx;
    this.vif.drv.last <= 'dx;
endtask: drive_idle

`endif
